package com.zzyl.service.impl;

import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.zzyl.enums.BasicEnum;
import com.zzyl.exception.BaseException;
import com.zzyl.properties.WechatConfigProperties;
import com.zzyl.service.WecharService;
import com.zzyl.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;


@Service
@Slf4j
public class WecharServiceImpl implements WecharService {

    private final String GET_OPEN_ID_URL = "https://api.weixin.qq.com/sns/jscode2session";

    private final String GET_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token";

    private final String GET_POST_PHONE_NUM_URL = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=";

    @Autowired
    private WechatConfigProperties wechatConfigProperties;

    /**
     * 通过授权码获取用户的OpenId
     * @param code 授权码，用于换取OpenId
     * @return 返回用户的OpenId字符串
     * @throws BaseException 如果获取OpenId失败，则抛出异常
     */
    @Override
    public String getOpenId(String code) {
        // 从应用配置中获取请求参数
        Map<String,Object> map = getAppConfig();
        // 添加必要的请求参数
        map.put("js_code",code);
        map.put("grant_type","authorization_code");
        // 向指定URL发送GET请求，并获取响应的JSON字符串
        String jsonStr = HttpUtil.get(GET_OPEN_ID_URL,map);
        log.info("获取openId的jsonStr：{}",jsonStr);
        // 解析JSON字符串
        JSONObject obj = JSONUtil.parseObj(jsonStr);
        if (obj == null){
            log.error("获取openId失败");
            // 如果解析失败，抛出异常
            throw new BaseException(BasicEnum.GET_OPENID_ERROR);
        }
        // 从JSON对象中提取OpenId
        String openid = obj.getStr("openid");
        log.info("openid={}",openid);
        // 返回OpenId
        return openid;
    }


    /**
     * 根据电话区号获取电话号码。
     * @param phoneCode 电话区号。
     * @return 返回通过区号获取到的电话号码。
     * @throws BaseException 如果获取电话号码失败，则抛出基础异常。
     */
    @Override
    public String getPhoneNum(String phoneCode) {
        // 获取访问接口所需的token
        String token = getToken();

        // 准备请求参数
        HashMap<String, Object> map = new HashMap<>();
        map.put("code",phoneCode);

        // 发起POST请求获取电话号码
        String jsonStr = HttpUtil.post(GET_POST_PHONE_NUM_URL+token,JSONUtil.toJsonStr(map));
        log.info("获取手机号的jsonStr：{}",jsonStr);

        // 解析JSON响应
        JSONObject jsonObject = JSONUtil.parseObj(jsonStr);
        if (jsonObject == null){
            log.error("获取手机号失败");
            throw new BaseException(BasicEnum.GET_PHONE_ERROR);
        }

        // 从响应中提取电话号码信息
        JSONObject phoneInfo = jsonObject.getJSONObject("phone_info");
        if (phoneInfo == null){
            log.error("获取手机号失败");
            throw new BaseException(BasicEnum.GET_PHONE_ERROR);
        }

        // 提取电话号码并返回
        String phoneNum = phoneInfo.getStr("phoneNumber");
        log.info("手机号={}",phoneNum);
        return phoneNum;
    }


    /**
     * 获取应用的访问令牌。
     * 该方法首先从应用配置中获取必要的信息，然后通过向指定URL发送请求来获取访问令牌。
     * 如果获取失败，会记录日志并抛出异常。
     *
     * @return 返回从服务器获取到的访问令牌字符串。
     * @throws BaseException 如果获取访问令牌过程中出现错误，抛出此异常。
     */
    private String getToken() {
        // 从应用配置中获取初始化信息
        Map<String, Object> map = getAppConfig();
        // 添加grant_type参数，指定获取令牌的类型
        map.put("grant_type","client_credential");
        // 向服务器发送请求，获取令牌
        String jsonStr = HttpUtil.get(GET_TOKEN_URL, map);
        log.info("获取token的jsonStr：{}",jsonStr);
        // 检查返回的JSON字符串是否为空
        if (StringUtils.isBlank(jsonStr)){
            log.error("获取accessToken失败");
            throw new BaseException(BasicEnum.GET_TOKEN_ERROR);
        }
        // 解析JSON字符串
        JSONObject jsonObject = JSONUtil.parseObj(jsonStr);
        if (jsonObject == null){
            log.error("获取token失败");
            throw new BaseException(BasicEnum.GET_TOKEN_ERROR);
        }
        // 从JSON对象中提取访问令牌
        String token = jsonObject.getStr("access_token");
        log.info("token={}",token);
        return token;
    }


    /**
     * 获取应用程序配置信息的函数。
     * 该方法不接受任何参数，返回一个包含微信配置属性的Map对象。
     *
     * @return Map<String, Object> 包含应用程序配置信息的键值对，主要为appid和secret。
     */
    private Map<String, Object> getAppConfig() {
        // 初始化用于存储请求URL参数的Map对象
        Map<String, Object> map  = new HashMap<>();
        // 将微信配置属性中的appid和secret添加到Map中
        map.put("appid",wechatConfigProperties.getAppid());
        map.put("secret",wechatConfigProperties.getSecret());
        return map;
    }

}
